﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Objects.DataClasses;
using System.Linq;
using System.Threading;
using Dispatcher.Extensions;

namespace Dispatcher.Helpers
{
    /// <summary>
    /// Lazy Loader.
    /// </summary>
    public static class LazyLoader
    {
        /// <summary>
        /// Entity Objects To Load.
        /// </summary>
        private static readonly List<object> EntityObjectsToLoad = new List<object>(); 

        /// <summary>
        /// Load thread.
        /// </summary>
        private static readonly Thread Thread = new Thread(LoadThread) { IsBackground = true };

        /// <summary>
        /// Initializes the <see cref="LazyLoader"/> class.
        /// </summary>
        static LazyLoader()
        {
            Thread.Start();
        }

        /// <summary>
        /// Occurs when [loaded].
        /// </summary>
        public static event EventHandler Loaded;

        /// <summary>
        /// Adds to load.
        /// </summary>
        /// <param name="items">The items.</param>
        public static void AddToLoad(IEnumerable<object> items)
        {
            EntityObjectsToLoad.Clear();
            EntityObjectsToLoad.AddRange(items);
        }

        /// <summary>
        /// Loads the thread.
        /// </summary>
        private static void LoadThread()
        {
            while (true)
            {
                var item = EntityObjectsToLoad.LastOrDefault();
                if (item == null)
                {
                    Thread.Sleep(100);
                }
                else
                {
                    EntityObjectsToLoad.Remove(item);
                    if (item is EntityObject)
                    {
                        var entityState = ((EntityObject)item).EntityState;
                        if (entityState != EntityState.Detached && entityState != EntityState.Added && entityState != EntityState.Deleted)
                        {
                            var entityObject = item as EntityObject;
                            EntityObjectsToLoad.Remove(entityObject);
                            var type = entityObject.GetType();
                            var relatedEndProperties =
                                type.GetProperties().Where(p => p.PropertyType.BaseType == typeof(RelatedEnd)).ToList();

                            foreach (var relatedEndProperty in relatedEndProperties)
                            {
                                var relatedEnd = (RelatedEnd)relatedEndProperty.GetValue(entityObject);
                                if (!relatedEnd.IsLoaded)
                                {
                                    relatedEnd.Load();
                                    if (Loaded != null)
                                    {
                                        Loaded(null, null);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
